package net.sunzc.yunlian.interact.device.mini;

import com.android.ddmlib.*;
import net.sunzc.yunlian.interact.device.CommandInteract;
import net.sunzc.yunlian.interact.device.command.CommonCommand;
import net.sunzc.yunlian.utils.Utils;
import org.apache.mina.core.RuntimeIoException;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.filterchain.IoFilter;
import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.executor.WriteRequestFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

/**
 * 这是个安装器
 * Created by Administrator on 2016/9/18.
 */
class MiniManager {
    private static final String CHMOD_COMMAND = "chmod 777 ";
    private static final String TAG = "MiniManager";
    private final CommandInteract mDevice;
    private String remoteMiniFile;
    private NioSocketConnector mConnector;
    private IoSession mSession;
    private MiniListener mListener;
    private InetSocketAddress remoteAddress;
    private String mStartCommand;
    private boolean isReconnect;

    MiniManager(CommandInteract device) {
        this.mDevice = device;
        initNio();
    }

    void install(File miniFile, boolean canExecute) {
        remoteMiniFile = "/data/local/tmp/" + miniFile.getName();
        mDevice.pushFile(miniFile.getAbsolutePath(), remoteMiniFile);
        Log.i(TAG, miniFile.getAbsolutePath() + "安装完成");
        if (!canExecute) return;
        try {
            mDevice.shellExecute(CommonCommand.getCommand(CHMOD_COMMAND + remoteMiniFile));
        } catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException | IOException e) {
            e.printStackTrace();
            Log.e(TAG, "权限修改失败");
        }
        Log.i(TAG, remoteMiniFile + "权限修改成功");
    }

    private void initNio() {
        mConnector = new NioSocketConnector();
        SocketSessionConfig config = mConnector.getSessionConfig();
        config.setReceiveBufferSize(4096);
        config.setTcpNoDelay(true);
        config.setKeepAlive(true);
        config.setSendBufferSize(1024);
        mConnector.setConnectTimeoutCheckInterval(20000);
        DefaultIoFilterChainBuilder filterChain = mConnector.getFilterChain();
        filterChain.addFirst("reconnect", new ReconnectFilter());
        filterChain.addLast("write", new WriteRequestFilter());
        mConnector.setHandler(new MiniHandler());
    }

    SocketSessionConfig configureSession() {
        return mConnector.getSessionConfig();
    }

    void addFilter(String name, IoFilter filter) {
        mConnector.getFilterChain().addAfter("reconnect", name, filter);
    }

    void stop() {
        isReconnect = false;
        if (mSession != null)
            mSession.closeNow();
    }

    void restart() {
        stop();
        Utils.threadPool.submit(this::connectByNio);
    }

    void send(String data) {
        CharsetEncoder en = Charset.forName("UTF-8").newEncoder();
        IoBuffer tmp_bf = IoBuffer.allocate(10).setAutoExpand(true);
        try {
            tmp_bf.putString(data, en);
        } catch (CharacterCodingException e) {
            e.printStackTrace();
        }
        tmp_bf.flip();
        if (mSession != null) {
            mSession.write(tmp_bf);
        } else {
            Log.i(TAG, "连接还未打开");
        }
    }

    void setOnReceiveListener(MiniListener listener) {
        this.mListener = listener;
    }

    void portForward(int port, String abstractName) {
        mDevice.createForward(port, abstractName, IDevice.DeviceUnixSocketNamespace.ABSTRACT);
        remoteAddress = new InetSocketAddress("localhost", port);
    }

    public void start(String command) {
        mStartCommand = command;
        Utils.threadPool.submit(() -> {
            try {
                mDevice.shellExecute(CommonCommand.getCommand(mStartCommand, 24 * 60));//在这里记得加超时限制
            } catch (TimeoutException | AdbCommandRejectedException | ShellCommandUnresponsiveException | IOException e) {
                e.printStackTrace();
            }
        });
        Utils.threadPool.submit(this::connectByNio);
    }

    private void connectByNio() {
        isReconnect = true;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (remoteAddress == null) {
            Log.e(TAG, "没有进行端口转发");
            return;
        }

        for (; ; ) {
            try {
                ConnectFuture future = mConnector.connect(remoteAddress);
                Log.i(TAG, "正在连接..." + remoteAddress.toString());
                future.awaitUninterruptibly();
                mSession = future.getSession();
                break;
            } catch (RuntimeIoException e) {
                Log.e(TAG, "连接失败，5秒后重新获取会话" + e.getMessage());
                e.printStackTrace();
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                    Log.e(TAG, "线程中断" + e.getMessage());
                }
            }
        }
    }

    public void start() {
        start(remoteMiniFile);
    }

    @Override
    public String toString() {
        return remoteMiniFile + "---" + mSession.getRemoteAddress();
    }

    interface MiniListener {
        void onReceive(Object message);
    }

    /**
     * 重连
     */
    private class ReconnectFilter extends IoFilterAdapter {
        @Override
        public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
            Log.e(TAG, MiniManager.this + "--会话" + session.getId() + "已经关闭");
            if (isReconnect) {
                Log.e(TAG, MiniManager.this + "--会话" + session.getId() + "--5s后，断线重连");
                connectByNio();
            }
        }

        @Override
        public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
            cause.printStackTrace();
            Log.e(TAG, MiniManager.this + "--会话" + session.getId() + "出错" + cause.getMessage());
        }
    }

    private class MiniHandler extends IoHandlerAdapter {
        @Override
        public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
            Log.e(TAG, MiniManager.this + "--会话" + session.getId() + "空闲" + status.toString());
            session.closeNow();
        }

        @Override
        public void sessionOpened(IoSession session) throws Exception {
            Log.i(TAG, MiniManager.this + "--会话" + session.getId() + "已经打开");
            mSession = session;
        }

        @Override
        public void messageSent(IoSession session, Object message) throws Exception {
            String sendStr = ((IoBuffer) message).getString(Charset.forName("UTF-8").newDecoder());
            Log.i(TAG, MiniManager.this + "--会话" + session.getId() + "--数据发送完成:" + sendStr);
        }

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
//            Log.i(TAG, MiniManager.this + "--会话" + session.getId() + "--接收数据:" + message);
            if (mListener != null) {
                mListener.onReceive(message);
            }
        }
    }
}
